<?php
//   +----------------------------------------------------------------------
//   | Copyright (c) 2015-2025 http://www.hdphp.cn All rights reserved.
//   | Licensed ( http://www.hdphp.cn/licenses/ )
//   | Author: Jack <sophia2152@qq.com>
//   | 官方网址: http://www.hdphp.cn
//   | 这不是一个自由软件！您只能在不用于商业目的的前提下对程序代码进行修改和使用。
//   | 任何企业和个人不允许对程序代码以任何形式任何目的再发布。
//   +----------------------------------------------------------------------
namespace hdphp\services;

use think\cache\Driver;
use think\cache\TagSet;
use think\facade\Cache;
use think\facade\Config;

/**
 * Class：缓存类
 * 创建人：Jack
 * 创建时间：2023/7/28 9:42
 * @package hdphp\services
 */
class CacheService
{
    /**
     * 标签名
     * @var string
     */
    protected static string $cacheName = 'hdphp';

    /**
     * 缓存队列key
     * @var string[]
     */
    protected static $redisQueueKey = [
        0 => 'product',
        1 => 'seckill',
        2 => 'bargain',
        3 => 'combination',
        4 => 'integral',
        5 => 'discounts',
        6 => 'lottery'
    ];

    /**
     * 过期时间
     * @var int
     */
    protected static $expire;

    /**
     * 注释：获取缓存过期时间
     * 创建人：Jack
     * 创建时间：2023/7/28 10:14
     * @param int|null $expire
     * @return int
     */
    protected static function getExpire(int $expire = null): int
    {
        if ($expire == null) {
            if (self::$expire) {
                return self::$expire;
            }
            $default = Config::get('cache.default');
            $expire = Config::get('cache.stores.' . $default . '.expire');
            if (!is_int($expire)) {
                $expire = (int)$expire;
            }
        }

        return self::$expire = $expire;
    }

    /**
     * 注释：缓存句柄
     * 创建人：Jack
     * 创建时间：2023/7/28 10:21
     * @param string|null $tag
     * @return TagSet
     */
    public static function handler(?string $tag = null)
    {
        return Cache::tag($tag ?: self::$cacheName);
    }

    /**
     * 注释：检查缓存是否存在
     * 创建人：Jack
     * 创建时间：2023/7/28 10:24
     * @param string $name
     * @return bool
     */
    public static function has(string $name): bool
    {
        try {
            return Cache::has($name);
        } catch (\Throwable $e) {
            return false;
        }
    }

    /**
     * 注释：写入缓存
     * 创建人：Jack
     * 创建时间：2023/7/28 10:27
     * @param string   $name   缓存名称
     * @param mixed    $value  缓存值
     * @param int|null $expire 缓存时间，为0读取系统缓存时间
     * @param string   $tag
     * @return bool
     */
    public static function set(string $name, $value, int $expire = null, string $tag = 'hdphp'): bool
    {
        try {
            return self::handler($tag)->set($name, $value, $expire ?? self::getExpire($expire));
        } catch (\Throwable $e) {
            return false;
        }
    }

    /**
     * 注释：读取缓存
     * 创建人：Jack
     * 创建时间：2023/7/28 10:33
     * @param string $name
     * @param mixed  $default
     * @return mixed|string
     */
    public static function get(string $name, $default = '')
    {
        return Cache::get($name) ?? $default;
    }

    /**
     * 注释：如果不存在则写入缓存
     * 创建人：Jack
     * 创建时间：2023/7/28 10:30
     * @param string   $name
     * @param mixed    $default
     * @param int|null $expire
     * @param string   $tag
     * @return false|mixed|null
     */
    public static function remember(string $name, $default = false, int $expire = null, string $tag = 'hdphp')
    {
        try {
            return self::handler($tag)->remember($name, $default, $expire ?? self::getExpire($expire));
        } catch (\Throwable $e) {
            try {
                if (is_callable($default)) {
                    return $default();
                } else {
                    return $default;
                }
            } catch (\Throwable $e) {
                return null;
            }
        }
    }

    /**
     * 注释：删除缓存
     * 创建人：Jack
     * 创建时间：2023/7/28 10:35
     * @param string $name
     * @return bool
     */
    public static function delete(string $name)
    {
        return Cache::delete($name);
    }

    /**
     * 注释：清空缓存池
     * 创建人：Jack
     * 创建时间：2023/7/28 10:35
     * @param string $tag
     * @return bool
     */
    public static function clear(string $tag = 'hdphp')
    {
        return self::handler($tag)->clear();
    }

    /**
     * 注释：Redis缓存句柄
     * 创建人：Jack
     * 创建时间：2023/7/28 11:02
     * @param string|null $type
     * @return Driver|TagSet
     */
    public static function redisHandler(string $type = null)
    {
        if ($type) {
            return Cache::store('redis')->tag($type);
        } else {
            return Cache::store('redis');
        }
    }

    /**
     * 注释：放入令牌桶
     * 创建人：Jack
     * 创建时间：2023/7/28 15:57
     * @param string $key
     * @param        $value
     * @param        $expire
     * @param string $type
     * @return bool
     */
    public static function setTokenBucket(string $key, $value, $expire = null, string $type = 'admin')
    {
        try {
            $redisCahce = self::redisHandler($type);
            return $redisCahce->set($key, $value, $expire);
        } catch (\Throwable $e) {
            return false;
        }
    }

    /**
     * 注释：清除所有令牌桶
     * 创建人：Jack
     * 创建时间：2023/7/28 16:15
     * @param string $type
     * @return bool
     */
    public static function clearTokenAll(string $type = 'admin')
    {
        try {
            return self::redisHandler($type)->clear();
        } catch (\Throwable $e) {
            return false;
        }
    }

    /**
     * 注释：清除令牌桶
     * 创建人：Jack
     * 创建时间：2023/7/28 16:15
     * @param string $key
     * @return bool
     */
    public static function clearToken(string $key)
    {
        try {
            return self::redisHandler()->delete($key);
        } catch (\Throwable $e) {
            return false;
        }
    }

    /**
     * 注释：查看令牌是否存在
     * 创建人：Jack
     * 创建时间：2023/7/28 16:15
     * @param string $key
     * @return bool
     */
    public static function hasToken(string $key)
    {
        try {
            return self::redisHandler()->has($key);
        } catch (\Throwable $e) {
            return false;
        }
    }

    /**
     * 注释：获取token令牌桶
     * 创建人：Jack
     * 创建时间：2023/7/28 16:15
     * @param string $key
     * @return mixed|null
     */
    public static function getTokenBucket(string $key)
    {
        try {
            return self::redisHandler()->get($key, null);
        } catch (\Throwable $e) {
            return null;
        }
    }

    /**
     * 注释：获取指定分数区间的成员
     * 创建人：Jack
     * 创建时间：2023/7/28 16:15
     * @param       $key
     * @param int   $start
     * @param int   $end
     * @param array $options
     * @return mixed
     */
    public static function zRangeByScore($key, $start = '-inf', $end = '+inf', array $options = [])
    {
        return self::redisHandler()->zRangeByScore($key, $start, $end, $options);
    }

    /**
     * 注释：设置redis入库队列
     * 创建人：Jack
     * 创建时间：2023/7/28 16:16
     * @param string $unique
     * @param int    $number
     * @param int    $type
     * @param bool   $isPush true :重置 false：累加
     * @return bool
     */
    public static function setStock(string $unique, int $number, int $type = 1, bool $isPush = true)
    {
        if (!$unique || !$number) return false;
        $name = (self::$redisQueueKey[$type] ?? '') . '_' . $type . '_' . $unique;
        /** @var self $cache */
        $cache = self::redisHandler();
        $res = true;
        if ($isPush) {
            $cache->del($name);
        }
        $data = [];
        for ($i = 1; $i <= $number; $i++) {
            $data[] = $i;
        }
        $res = $res && $cache->lPush($name, ...$data);
        return $res;
    }

    /**
     * 注释：弹出redis队列中的库存条数
     * 创建人：Jack
     * 创建时间：2023/7/28 16:16
     * @param string $unique
     * @param int    $number
     * @param int    $type
     * @return bool
     */
    public static function popStock(string $unique, int $number, int $type = 1)
    {
        if (!$unique || !$number) return false;
        $name = (self::$redisQueueKey[$type] ?? '') . '_' . $type . '_' . $unique;
        /** @var self $cache */
        $cache = self::redisHandler();
        $res = true;
        if ($number > $cache->lLen($name)) {
            return false;
        }
        for ($i = 1; $i <= $number; $i++) {
            $res = $res && $cache->lPop($name);
        }

        return $res;
    }

    /**
     * 注释：是否有库存|返回库存
     * 创建人：Jack
     * 创建时间：2023/7/28 16:17
     * @param string $unique
     * @param int    $number
     * @param int    $type
     * @return bool
     */
    public static function checkStock(string $unique, int $number = 0, int $type = 1)
    {
        $name = (self::$redisQueueKey[$type] ?? '') . '_' . $type . '_' . $unique;
        if ($number) {
            return self::redisHandler()->lLen($name) >= $number;
        } else {
            return self::redisHandler()->lLen($name);
        }
    }

    /**
     * 注释：检查锁
     * 创建人：Jack
     * 创建时间：2023/7/28 10:38
     * @param string $key
     * @param int    $timeout
     * @return bool
     */
    public static function setMutex(string $key, int $timeout = 10)
    {
        $curTime = time();
        $readMutexKey = "redis:mutex:{$key}";
        $mutexRes = self::redisHandler()->handler()->setnx($readMutexKey, $curTime + $timeout);
        if ($mutexRes) {
            return true;
        }
        // 就算意外退出，下次进来也会检查key，防止死锁
        $time = self::redisHandler()->handler()->get($readMutexKey);
        if ($curTime > $time) {
            self::redisHandler()->handler()->del($readMutexKey);
            return self::redisHandler()->handler()->setnx($readMutexKey, $curTime + $timeout);
        }

        return false;
    }

    /**
     * 注释：删除锁
     * 创建人：Jack
     * 创建时间：2023/7/28 10:41
     * @param string $key
     * @return void
     */
    public static function delMutex(string $key)
    {
        $readMutexKey = "redis:mutex:{$key}";
        self::redisHandler()->handler()->del($readMutexKey);
    }

    /**
     * 注释：数据库锁
     * 创建人：Jack
     * 创建时间：2023/7/28 16:18
     * @param     $key
     * @param     $fn
     * @param int $ex
     * @return mixed
     */
    public static function lock($key, $fn, int $ex = 6)
    {
        if (Config::get('cache.default') == 'file') {
            return $fn();
        }

        return app()->make(LockService::class)->exec($key, $fn, $ex);
    }

    /**
     * 注释：魔术方法
     * 创建人：Jack
     * 创建时间：2023/7/28 10:42
     * @param $name
     * @param $arguments
     * @return mixed
     */
    public static function __callStatic($name, $arguments)
    {
        return self::redisHandler()->{$name}(...$arguments);
    }

    /**
     * 注释：魔术方法
     * 创建人：Jack
     * 创建时间：2023/7/28 10:43
     * @param $name
     * @param $arguments
     * @return mixed
     */
    public function __call($name, $arguments)
    {
        return self::redisHandler()->{$name}(...$arguments);
    }
}